package fpzhan.plane.program.param;

import fpzhan.plane.program.function.CodeBlockFunction;
import fpzhan.plane.program.util.StringUtils;
import fpzhan.plane.program.connect.AddParamTool;
import fpzhan.plane.program.exception.ExecuteParamToolException;

import java.util.Objects;
import java.util.Set;
import java.util.function.*;

public final class ExecuteParamTool<T> extends AddParamTool<T> {


    protected ExecuteParamTool(String getKey, Supplier<T> supplier) throws ExecuteParamToolException {
        super(getKey, supplier);
    }

    public ExecuteParamTool(String getKey, ParamTool<T>.Operate operate, Set<String> adds) {
        super(getKey, operate, adds);
    }

    public <E> ExecuteParamTool<T> update(BiConsumer<T,E> consumer, E one){
        Objects.requireNonNull(consumer);
        operate.add(BiConsumer.class.getName(),consumer,one);
        return this;
    }

    public <E> ExecuteParamTool<T> updateLink(BiConsumer<T,E> consumer, String key){
        Objects.requireNonNull(consumer);
        operate.add(BiConsumer.class.getName(),consumer,null,key);
        return this;
    }


    @Override
    public ExecuteParamTool<T> add(String saveKey){
        Objects.requireNonNull(saveKey);
        if(getAdds()==null){
            initAdds();
        }
        getAdds().add(saveKey);
        operate.add(String.class.getName(),null,saveKey,null);
        return this;
    }

    @Override
    protected CodeBlockFunction end(){
        return param -> {
            Object obj;
            int j=0;
            if (getKey!=null){
                obj=param.get(getKey);
            }else{
                obj=((Supplier)operate.getChains().get(0)).get();
                j++;
            }
            for(int i=j;i<operate.getChains().size();i++){
                if(Supplier.class.getName().equals(operate.getFunctionTypes().get(i))){
                    obj=((Supplier)operate.getChains().get(i)).get();
                }else if(Function.class.getName().equals(operate.getFunctionTypes().get(i))){
                    obj=((Function)operate.getChains().get(i)).apply(obj);
                }else if(BiConsumer.class.getName().equals(operate.getFunctionTypes().get(i))){
                    if(StringUtils.isNotEmpty(operate.getLinks().get(i))){
                        ((BiConsumer)operate.getChains().get(i)).accept(obj,param.get(operate.getLinks().get(i)));
                    }else{
                        ((BiConsumer)operate.getChains().get(i)).accept(obj,operate.getParams().get(i));
                    }
                }else if(String.class.getName().equals(operate.getFunctionTypes().get(i))){
                    param.put(operate.getParams().get(i).toString(),obj);
                }else if (BiFunction.class.getName().equals(operate.getFunctionTypes().get(i))){
                    if(StringUtils.isNotEmpty(operate.getLinks().get(i))){
                        obj=((BiFunction)operate.getChains().get(i)).apply(obj,param.get(operate.getLinks().get(i)));
                    }else{
                        obj=((BiFunction)operate.getChains().get(i)).apply(obj,operate.getParams().get(i));
                    }
                }
            }
        };
    }

    @Override
    public <E> ExecuteParamTool<E> then(Function<T, E> function) {
        getOperate().add(Function.class.getName(),function,null);
        return new ExecuteParamTool<E>(getKey,(ParamTool<E>.Operate)getOperate(),getAdds());
    }

    @Override
    public <R, E> ExecuteParamTool<E> then(BiFunction<T, R, E> biFunction, R r) {
        getOperate().add(BiFunction.class.getName(),biFunction,r);
        return new ExecuteParamTool<E>(getKey,(ParamTool<E>.Operate)getOperate(),getAdds());
    }

    @Override
    public <E, R> ExecuteParamTool<E> thenLink(BiFunction<T, R, E> biFunction, String key) {
        getOperate().add(BiFunction.class.getName(),biFunction,null,key);
        return new ExecuteParamTool<E>(getKey,(ParamTool<E>.Operate)getOperate(),getAdds());
    }
}
